package wsi.task.map;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.rygh.core.html.Html;
import org.rygh.core.html.element.HtmlAnchor;
import org.rygh.core.html.element.HtmlDiv;
import org.rygh.core.html.element.HtmlOl;
import org.rygh.core.html.element.HtmlQuote;
import org.rygh.core.html.element.HtmlWrapper;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.model.common.MapList;
import org.rygh.core.util.UIo;
import org.rygh.core.xml.XmlPath;
import org.rygh.semantic.gui.swing.input.Input;
import org.rygh.semantic.model.ex.FactSet;
import org.rygh.semantic.model.rdf.Entity;

import wsi.dataguide.tree.WSXmlLeaf;
import wsi.dataguide.tree.WSXmlParser;
import wsi.dataguide.tree.WSXmlTree;
import wsi.function.DirManager;
import wsi.function.Function.Phrase;
import wsi.script.AppConfig;
import wsi.subgraph.ExtractWorker;
import wsi.subgraph.tree.EntityBranch;
import wsi.subgraph.tree.EntityLeaf;
import wsi.subgraph.tree.EntityTree;
import wsi.subgraph.tree.EntityTreeLoader;
import wsi.task.TaskRoutine;
import wsi.task.input.InputLocation;
import wsi.task.input.InputLocator;

public class OldMapper extends TaskRoutine {

	private final MapList<Input, MapCandidate> mcMap = new MapList<>();
	private final Map<Input, MapCandidateCache> mcCacheMap = new HashMap<>();
	private final MappingStatistics statistics = new MappingStatistics();

	private EntityTree entityTree;

	public OldMapper(DirManager dir) {
		super(dir);

	}

	@Override
	public void compute() {
		WSXmlParser parser;
		WSXmlTree xmltree;
		String str;
		Entity entity;
		EntityTreeLoader loader;

		Input[] inputs = dir.getInputs();

		ExtractWorker yagohop = new ExtractWorker(dir, null);
		FactSet facts;
		String fname;

		for (Input input : inputs) {
			log.info("processing input " + input);

			fname = input.toFilename();

			facts = yagohop.load(fname);
			if (facts == null || facts.isEmpty())
				continue;

			entity = input.getEntity();
			loader = new EntityTreeLoader(entity, facts);
			entityTree = loader.load();

			str = UIo.readFile(new File(dir.getXmlDir(), fname + ".xml"));
			parser = new WSXmlParser(input, str, false);

			xmltree = parser.parse();
			log.info("xml tree passed.");

			// System.out.println("xmltree " + xmltree);

			map2(xmltree, input);

			if (function.isSkipped(Phrase.REST)) {
				log.info("break for trial.");
				break;
			}
		}
	}

	private void expandFrom(WSXmlLeaf node, String prefix, MapCandidate mc) {
		// System.out
		// .printf("expandA %s %s\n", node.getObject().getName(), prefix);

		// first go down

		// if (!node.isLeaf()) {
		// assert false;
		// }

		// then go up

		WSXmlLeaf parent = node.getParent();
		WSXmlLeaf _node;
		String new_prefix;
		int index;

		for (int i = 0; i < parent.getChildren().size(); i++) {
			_node = parent.getChildren().get(i);

			if (_node == node) {
				// System.out.printf("skipped %s\n",
				// _node.getObject().getName());
				continue;
			}

			index = parent.getIndex(_node);
			new_prefix = String.format("%s/%s[%d]", prefix, _node.getValue()
					.getName(), index);

			expandFrom2(_node, new_prefix, mc);
		}

	}

	private void expandFrom2(WSXmlLeaf node, String prefix, MapCandidate mc) {
		// System.out
		// .printf("expandB %s %s\n", node.getObject().getName(), prefix);

		if (node.isLeaf()) {
			// log.info(String.format("OBTAINED %s %s",
			// node.getObject().getName(), prefix));
			mc.addXPath(node, new XmlPath(prefix));
			return;
		}

		WSXmlLeaf _node;
		String new_prefix;
		int index;

		for (int i = 0; i < node.getChildren().size(); i++) {
			_node = node.getChildren().get(i);

			index = node.getIndex(_node);
			new_prefix = String.format("%s/%s[%d]", prefix, _node.getValue()
					.getName(), index);

			expandFrom2(_node, new_prefix, mc);
		}
	}

	private void link(EntityLeaf s, EntityLeaf d, List<EntityLeaf> slist,
			List<EntityLeaf> dlist) {
		log.info(String.format("link source %s\n\t   destin %s", s.getValue(),
				d.getValue()));

		slist.clear();
		dlist.clear();

		while (s != d) {

			if (s.getLevel() == d.getLevel()) {
				slist.add(s);
				dlist.add(d);

				s = s.getParent();
				d = d.getParent();
			} else if (s.getLevel() > d.getLevel()) {
				slist.add(s);

				s = s.getParent();
			} else if (s.getLevel() < d.getLevel()) {
				dlist.add(d);

				d = d.getParent();
			}
		}

		slist.add(s);
		dlist.add(d);

		System.out.println("slist " + slist);
		System.out.println("dlist " + dlist);
	}

	private void map2(WSXmlTree xmltree, Input input) {
		InputLocator locator = new InputLocator(xmltree, false);
		locator.setInput(input);
		locator.compute();

		List<InputLocation> locs = locator.getLocations();
		List<MapCandidate> mcList = new ArrayList<>();
		MapCandidateCache mcCache = new MapCandidateCache();
		MapCandidate mc = null;
		WSXmlLeaf node;
		String prefix;

		log.info(String.format("%d input locations identified in returned XML",
				locs.size()));

		for (InputLocation i : locs) {
			log.info("InputLocation " + i);

			node = i.getNode();

			if (mc != null) {
				if (mc.isSourceContentEqualTo(node)) {
					mc.addSource(node);
					continue;
				}
			}

			mc = new MapCandidate(node);
			mc.setCache(mcCache);
			mc.setLocation(i);
			prefix = "..";

			while (!node.isRoot()) {
				expandFrom(node, prefix, mc);

				node = node.getParent();
				prefix += "/..";
			}

			log.info(String.format("found %d map candidates for %s.", mc
					.getXPathMap().size(), mc.getSource().getValue().getName()));

			map3(mc);
			mcList.add(mc);

			// System.out.println("mapped branches " + mc.getBranchMap());
		}

		log.info(String.format("mapping for %s completed, %d mc.", input,
				mcList.size()));

		for (MapCandidate i : mcList) {
			statistics.setMapCandidate(i);
			statistics.update();
		}

		mcMap.put(input, mcList);
		mcCacheMap.put(input, mcCache);
	}

	private void map3(MapCandidate mc) {
		log.info("to map the following pairs: " + mc);

		WSXmlLeaf source = mc.getSource();
		Map<WSXmlLeaf, XmlPath> leafMap = mc.getXPathMap();

		WSXmlLeaf dest;
		XmlPath xpath;

		String source_value = source.getValue().getValue();
		List<EntityLeaf> snodes, dnodes;

		snodes = new ArrayList<>(entityTree.getNodes(source_value, false));
		assert (snodes != null && !snodes.isEmpty()) : source_value;

		/*
		 * we remove non-entity nodes for source node candidates.
		 */
		List<EntityLeaf> nonEntityList = new ArrayList<>();

		for (EntityLeaf i : snodes)
			if (!i.getValue().getValue().hasPrefix())
				nonEntityList.add(i);

		snodes.removeAll(nonEntityList);

		log.info(String.format("source %s %s\n      snodes %s\n", source_value,
				source.getXpath(), snodes));

		assert !snodes.isEmpty();

		List<EntityLeaf> slist = new ArrayList<>();
		List<EntityLeaf> dlist = new ArrayList<>();
		EntityBranch branch;
		String dest_value;
		MapCandidateCache cache = mc.getCache();

		for (Entry<WSXmlLeaf, XmlPath> e : leafMap.entrySet()) {
			dest = e.getKey();
			xpath = e.getValue();

			dest_value = dest.getValue().getValue();
			dnodes = entityTree.getNodes(dest_value, false);

			if (dnodes == null)
				continue;

			log.info(String.format("destin %s %s \n      dnodes %s\n",
					dest_value, xpath, dnodes));

			mc.addDestNode(dest);

			if (cache.contains(cache.getStringSetKey(source, dest))) {
				log.info("skipped by cache");
				continue;
			}

			for (EntityLeaf s : snodes) {
				for (EntityLeaf d : dnodes) {

					if (s == d)
						continue;

					slist.clear();
					dlist.clear();
					link(s, d, slist, dlist);

					branch = new EntityBranch(slist, dlist);
					mc.addBranch(dest, branch);

					System.out.println("branch \n" + branch);
				}
			}

		}
	}

	@Override
	public String save() {
		UIo.saveFile(
				new File(dir.getDebugDir(), String.format(
						"_debug_mapping_%d_%.3f.html", function.getHop(),
						AppConfig.shared().getFunctionalityThreshold())),
				toString());

		return "";
	}

	@Override
	public String toString() {
		Html html = new Html(dir.getServiceName() + "/" + function.getName());
		HtmlTable table;
		HtmlAnchor anchor;
		HtmlOl ol = new HtmlOl();
		Map<Input, HtmlAnchor> anchorMap = new HashMap<>();

		for (Input e : mcMap.keySet()) {
			anchor = new HtmlAnchor(e.getValue());

			ol.addElement(anchor);
			anchorMap.put(e, anchor);
		}

		html.addElement(ol);

		/**/

		html.addElement(statistics.getHtmlTable());
		html.newline();

		/**/

		TableData data;
		TableTuple tuple;
		// MapList<XmlLeaf, EntityBranch> bmap;
		Map<WSXmlLeaf, XmlPath> xpathmap;
		MapList<String, WSXmlLeaf> gmap;
		int k;

		for (Entry<Input, List<MapCandidate>> e : mcMap.entrySet()) {

			html.addElement(new HtmlWrapper(anchorMap.get(e.getKey())
					.toAnchorString()));
			html.addElement(new HtmlDiv(new HtmlQuote(String.format(
					"<b>%s</b>: %d different input values identified.",
					e.getKey(), e.getValue().size()))));

			for (MapCandidate i : e.getValue()) {
				data = new TableData();

				tuple = new TableTuple();
				tuple.addBlank();
				tuple.add("Tag");
				tuple.add("XPath");
				tuple.add("Value");
				tuple.add("Triples");
				data.add(tuple);

				k = 1;
				for (WSXmlLeaf m : i.getSources()) {
					tuple = new TableTuple();
					tuple.add(String.format("S<sub>%d</sub>", k++));
					tuple.add(m.getValue().getName());
					tuple.add(m.getXpath().getValue());
					tuple.add(String.format("<i>%s</i>", m.getValue()
							.getValue()));
					tuple.add("");
					data.add(tuple);
				}

				// bmap = i.getBranchMap();
				xpathmap = i.getXPathMap();
				gmap = i.getLeafGroupMap();
				k = 1;

				for (Entry<String, List<WSXmlLeaf>> j : gmap.entrySet()) {

					for (WSXmlLeaf l : j.getValue()) {
						tuple = new TableTuple();
						tuple.add(k);
						tuple.add(l.getValue().getName());
						tuple.add(xpathmap.get(l).getValue());
						tuple.add(String.format("<i>%s</i>", j.getKey()));
						tuple.add(String.format("<small>%s</small>",
								i.toTripleString(l)));
						data.add(tuple);
						k++;
					}
				}

				table = new HtmlTable("", data, false);
				html.addElement(table);
				html.newline();
			}
		}

		return html.toString();
	}
}
